Animation of plots in R can be fairly straightforward. It can also be stunningly complex. We’ll focus on the straightforward ones - you’ll have to wrap your head around some of the lingo, but once you get the drift it’s not that difficult.

We’ll be using gganimate to do our animating. If you don’t already have this or gifski and av as an installed library, you’ll want to do that (these are what support the creation of GIF and movie files respectively. Yet again, we’ll be using the gapminder dataset.

Animated Bubble Chart

Load the necessary libraries:

# install.packages(c("gifski", "av", "gganimate"))

library(gapminder)
library(tidyverse)
library(gganimate)

gganimate is built on top of ggplot, so the general approach is to first get a solid static ggplot visualization that we can use as our base, and once we have that, we animate it. Let’s begin with a robust, but completely standard ggplot call:

  1. Call ggplot to use the gapminder data, with gdpPercap mapped to X axis, lifeExp to Y, pop to size, and continent to color
  2. Make it a scatterplot, since there are a lot of points, let’s give it some transparency by setting the alpha to 0.7, and let’s hide the legend.
  3. The data came with pre-set colors for each country, let’s use it.
  4. Let’s scale the size of the points to something reasonable, 2 on the low end and 12 at max.
  5. Put the X axis on a log scale.
  6. Facet by continent
  7. Set our labels.
p1 <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = country)) +
   geom_point(alpha = 0.7, show.legend = FALSE) +
   scale_color_manual(values = country_colors) +
   scale_size(range = c(2, 12)) +
   scale_x_log10() +
   facet_wrap(~continent) +
   theme_bw() +
   labs(title = "Year: 1952-2007", x = "GPD per capita", y = "Life Expectancy")

print(p1)

To turn it into an animation, we simply add a few functions:

  1. a new labs function overwrites the previous one, so we can dynamically display the changing years as the data points move across the plot. Note the curly brackets enclosing the variable frame_time that will allow the year to dynamically display.
  2. the transition_time function takes in the year variable as an input and it allows the animated plot to transition frame by frame as a function of the year variable.
  3. ease_aes function takes in linear as an input argument and it defines how the transition occurs from frame to frame (in this case, linear). More on this below…
  4. since we saved this to p2, we now need to explicitly display it, so we call the animate function.
  5. the anim_save function allows the animated plot to be rendered to a .GIF file.
p2 <- p1 +
   labs(title = "Year: {frame_time}", x = "GDP per capita", y = "Life Expectancy") +
   transition_time(year) +
   ease_aes('linear')

animate(p2)

anim_save("gapminder1.gif")

The ease_aes function defines how a value changes to another value during it’s animated transition from one state to another. Will it progress linearly, or maybe start slowly and then build up momentum? Your ease function will determine that. Here are the available options and what they do:

Ok, you’re thinking…I have no idea what any of that actually means. Neither do I really. So just use this resource that can give you a sense of how each of these easing functions behave: https://easings.net/

There are also modifiers you can apply to these ease functions: -in The easing function is applied as-is -out The easing function is applied in reverse -in-out The first half of the transition it is applied as-is, while in the last half it is reversed

I wouldn’t overthink this - just choose something that looks good to you!

Show preceding frames with gradual falloff

We can use shadow_wake() to draw a small wake after the data by showing the latest frames up to the current. You can choose to gradually diminish the size and/or opacity of the shadow. The length of the wake is not given in absolute frames, it is given as a proportion of the total length of the animation, so the example below gives us a wake of points from the last 30% of frames. The alpha value is set here to FALSE so that the shadows are not transparent, but you can either set that to TRUE or a numeric (0-1) indicating what the alpha should be. Notice that we are simply layering on a shadow_wake() function call to the previously saved p2 object.

p3 <- p2 + 
   shadow_wake(wake_length = 0.3, alpha = FALSE)

animate(p3)

anim_save("gapminder2.gif")

Show the original data as trail

Alternatively we can use shadow_trail() to show the original data as a trail. The parameter distance means the animation will keep the points from 30% of the frames, spaced as evenly as possible.

p4 <- p2 +
   shadow_trail(distance = 0.3)

animate(p4)

anim_save("gapminder3.gif")

Reveal data along a given dimension

We can also use gganimate to reveal data along a specific dimension. This is most useful for time series data in which you can show the change over time. Below we’ve created a standard line plot of lifeExp by country, filtered to just show countries in Asia.

p5 <- ggplot(gapminder %>% filter(continent == "Asia"), aes(year, lifeExp, color = country)) +
   geom_line(show.legend = FALSE) +
   labs(title = "Life Expectancy Over Time - Asian Continent")

p5

We can then call transition_reveal to let the data gradually appear, by year. The geom_point call means that as it appears it shows a point.

p6 <- p5 + 
   geom_point(show.legend = FALSE) +
   transition_reveal(year)

animate(p6)

anim_save("gapminder4.gif")

Morphing Bar Charts

There may also be times when animating bar charts could be useful. Here we create a bar chart and then add an additional aesthetic called transition_states that provides a frame variable of year. For each value of the variable, a step on the chart will be drawn. The transition_length tells us how long the transition should be and the state_length is how long it rests at a particular state. Here they are set to be equal. Notice that we’ve also changed up our ease_aes function to “sine-in-out” (not for any particular reason).

p7 <- gapminder %>% 
   group_by(year, continent) %>% 
   summarize(cont_pop = sum(pop)) %>% 
   ggplot(aes(continent, cont_pop, fill = continent)) +
   geom_bar(stat = "identity") +
   transition_states(year, transition_length = 2, state_length = 2) +
   ease_aes('sine-in-out') +
   labs(title = "Population in {closest_state}")

animate(p7)

anim_save("gapminder5.gif")

We could just as easily have used the transition_time function here since we are using time as our animating variable. If we did that, our label would instead reference {frame_time} instead of {closest_state} and we would NOT have control over the transition length or state length. We wouldn’t have that control because for transition_time gganimate treats the time variable as continuous, so the transition length is based on the actual values. Notice the difference between the two!

p8 <- gapminder %>% 
   group_by(year, continent) %>% 
   summarize(cont_pop = sum(pop)) %>% 
   ggplot(aes(continent, cont_pop, fill = continent)) +
   geom_bar(stat = "identity") +
   transition_time(year) +
   ease_aes('sine-in-out') +
   labs(title = "Population in {frame_time}")
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
animate(p8)

anim_save("gapminder6.gif")

Dynamic Maps with Leaflet

Leaflet is a powerful open-source JavaScript library for building interactive maps in HTML.

The architecture is very similar to ggplot2, but instead of putting data-based layers on top of a static map, leaflet allows you to put data-based layers on top of an interactive map.

A leaflet map widget is created with the leaflet() command. We then add layers to the widget. The first layer that we will add is a tile layer containing all of the static map information, which by default comes from OpenStreetMap. The second layer we will add here is a marker, which designates a point location. Notice how the addMarkers() function can take a data argument, just like a geom_*() layer in ggplot2 would.

Below, we get started by creating a data frame containing the White House and then call tidygeocoder’s geocode function to get lat and long. After loading the leaflet library, we create a new object by calling leaflet to create a widget, add_tiles and finally addMarkers in which we designate the data set.

white_house <- tibble(
   address = "The White House, Washington, DC"
) %>% 
   tidygeocoder::geocode(address, method = "osm")

library(leaflet)

white_house_map <- leaflet() %>% 
   addTiles() %>% 
   addMarkers(data = white_house)

white_house_map

You can scroll and zoom at will!

You can also add a pop-up to provide more information about a particular location. Notice how we only need to call the previously saved leaflet map and then add a Popup layer to it.

white_house <- white_house %>% 
   mutate(title = "The White House", 
          street_address = "1600 Pennsylvania Ave")

white_house_map %>% 
   addPopups(data = white_house, 
             popup = ~paste0("<b>", title, "</b></br>", street_address))

There are several different providers of tiles. Below we’ll demonstrate two others, and we’ll also see how we can set a specific view and zoom level by giving it a lat and long and designating the zoom level desired.

# Background 1: NASA
leaflet() %>% 
   addTiles() %>% 
   setView(lng = 2.34, lat = 48.85, zoom = 5) %>% 
   addProviderTiles("NASAGIBS.ViirsEarthAtNight2012")
# Background 2: World Imagery
leaflet() %>% 
   addTiles() %>% 
   setView(lng = 2.34, lat = 48.85, zoom = 3) %>% 
   addProviderTiles("Esri.WorldImagery")

Here are some especially popular provider tiles that Leaflet provides:

  • Nasa: NASAGIBS.ViirsEarthAtNight2012
  • Google map: Esri.WorldImagery
  • Gray: Esri.WorldGrayCanvas
  • Terrain: Esri.WorldTerrain
  • Topo Map: Esri.WorldTopoMap

And this is a great website where you can preview all the available ones.

Choropleth Maps

You can create choropleth maps in Leaflet. Here we’ll be showing 2016 House election results in NC using the fec16 package that has detailed election results. We call their results_house dataset, do some clean up and then join it into their candidates dataset. From there we filter to North Carolina, group by the district and create some summary variables for each CD.

library(socviz)

nc_results <- county_data %>% # built in soviz data
   select(state, id, name, per_gop_2016) %>% 
   filter(state == "NC",
          name != "34")

nc_results
##     state    id                name per_gop_2016
## 1      NC 37001     Alamance County    0.5520184
## 2      NC 37003    Alexander County    0.7676420
## 3      NC 37005    Alleghany County    0.7266974
## 4      NC 37007        Anson County    0.4310179
## 5      NC 37009         Ashe County    0.7107143
## 6      NC 37011        Avery County    0.7724566
## 7      NC 37013     Beaufort County    0.6135006
## 8      NC 37015       Bertie County    0.3705757
## 9      NC 37017       Bladen County    0.5406212
## 10     NC 37019    Brunswick County    0.6310311
## 11     NC 37021     Buncombe County    0.4114210
## 12     NC 37023        Burke County    0.6839099
## 13     NC 37025     Cabarrus County    0.5848663
## 14     NC 37027     Caldwell County    0.7412930
## 15     NC 37029       Camden County    0.7141122
## 16     NC 37031     Carteret County    0.7104201
## 17     NC 37033      Caswell County    0.5475987
## 18     NC 37035      Catawba County    0.6763104
## 19     NC 37037      Chatham County    0.4353122
## 20     NC 37039     Cherokee County    0.7727994
## 21     NC 37041       Chowan County    0.5603545
## 22     NC 37043         Clay County    0.7468139
## 23     NC 37045    Cleveland County    0.6433151
## 24     NC 37047     Columbus County    0.6039963
## 25     NC 37049       Craven County    0.5960503
## 26     NC 37051   Cumberland County    0.4066896
## 27     NC 37053    Currituck County    0.7300974
## 28     NC 37055         Dare County    0.5935609
## 29     NC 37057     Davidson County    0.7341730
## 30     NC 37059        Davie County    0.7260890
## 31     NC 37061       Duplin County    0.5898257
## 32     NC 37063       Durham County    0.1851896
## 33     NC 37065    Edgecombe County    0.3333874
## 34     NC 37067      Forsyth County    0.4335600
## 35     NC 37069     Franklin County    0.5457829
## 36     NC 37071       Gaston County    0.6478940
## 37     NC 37073        Gates County    0.5350976
## 38     NC 37075       Graham County    0.7962873
## 39     NC 37077    Granville County    0.5014859
## 40     NC 37079       Greene County    0.5438662
## 41     NC 37081     Guilford County    0.3866165
## 42     NC 37083      Halifax County    0.3598730
## 43     NC 37085      Harnett County    0.6067846
## 44     NC 37087      Haywood County    0.6248839
## 45     NC 37089    Henderson County    0.6255061
## 46     NC 37091     Hertford County    0.3050495
## 47     NC 37093         Hoke County    0.4301003
## 48     NC 37095         Hyde County    0.5609327
## 49     NC 37097      Iredell County    0.6709527
## 50     NC 37099      Jackson County    0.5391024
## 51     NC 37101     Johnston County    0.6396225
## 52     NC 37103        Jones County    0.5836475
## 53     NC 37105          Lee County    0.5521559
## 54     NC 37107       Lenoir County    0.5236863
## 55     NC 37109      Lincoln County    0.7263302
## 56     NC 37111     McDowell County    0.7422918
## 57     NC 37113        Macon County    0.6942201
## 58     NC 37115      Madison County    0.6141711
## 59     NC 37117       Martin County    0.4956126
## 60     NC 37119  Mecklenburg County    0.3341092
## 61     NC 37121     Mitchell County    0.7832159
## 62     NC 37123   Montgomery County    0.6175212
## 63     NC 37125        Moore County    0.6340426
## 64     NC 37127         Nash County    0.4934013
## 65     NC 37129  New Hanover County    0.5027268
## 66     NC 37131  Northampton County    0.3643069
## 67     NC 37133       Onslow County    0.6564193
## 68     NC 37135       Orange County    0.2301516
## 69     NC 37137      Pamlico County    0.6238925
## 70     NC 37139   Pasquotank County    0.4764207
## 71     NC 37141       Pender County    0.6396646
## 72     NC 37143   Perquimans County    0.6282032
## 73     NC 37145       Person County    0.5758690
## 74     NC 37147         Pitt County    0.4496448
## 75     NC 37149         Polk County    0.6283689
## 76     NC 37151     Randolph County    0.7727108
## 77     NC 37153     Richmond County    0.5421251
## 78     NC 37155      Robeson County    0.5143712
## 79     NC 37157   Rockingham County    0.6396046
## 80     NC 37159        Rowan County    0.6719934
## 81     NC 37161   Rutherford County    0.7288671
## 82     NC 37163      Sampson County    0.5757469
## 83     NC 37165     Scotland County    0.4520119
## 84     NC 37167       Stanly County    0.7398051
## 85     NC 37169       Stokes County    0.7653052
## 86     NC 37171        Surry County    0.7420333
## 87     NC 37173        Swain County    0.5946172
## 88     NC 37175 Transylvania County    0.5986812
## 89     NC 37177      Tyrrell County    0.5694118
## 90     NC 37179        Union County    0.6396482
## 91     NC 37181        Vance County    0.3695982
## 92     NC 37183         Wake County    0.3789232
## 93     NC 37185       Warren County    0.3261879
## 94     NC 37187   Washington County    0.4186427
## 95     NC 37189      Watauga County    0.4699851
## 96     NC 37191        Wayne County    0.5491329
## 97     NC 37193       Wilkes County    0.7655622
## 98     NC 37195       Wilson County    0.4631181
## 99     NC 37197       Yadkin County    0.7962536
## 100    NC 37199       Yancey County    0.6491639

Now we need a NC counties shapefile. Remember that we can go to the tigris package here for this. We also need to load up the sf library so we can work with sf data.

library(sf)
library(tigris)
nc_map <- counties(state = c("NC"))
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |                                                                      |   1%
  |                                                                            
  |=                                                                     |   1%
  |                                                                            
  |=                                                                     |   2%
  |                                                                            
  |==                                                                    |   2%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |==                                                                    |   4%
  |                                                                            
  |===                                                                   |   4%
  |                                                                            
  |===                                                                   |   5%
  |                                                                            
  |====                                                                  |   5%
  |                                                                            
  |====                                                                  |   6%
  |                                                                            
  |=====                                                                 |   6%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |=====                                                                 |   8%
  |                                                                            
  |======                                                                |   8%
  |                                                                            
  |======                                                                |   9%
  |                                                                            
  |=======                                                               |   9%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |=======                                                               |  11%
  |                                                                            
  |========                                                              |  11%
  |                                                                            
  |========                                                              |  12%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |=========                                                             |  14%
  |                                                                            
  |==========                                                            |  14%
  |                                                                            
  |==========                                                            |  15%
  |                                                                            
  |===========                                                           |  15%
  |                                                                            
  |===========                                                           |  16%
  |                                                                            
  |============                                                          |  16%
  |                                                                            
  |============                                                          |  17%
  |                                                                            
  |============                                                          |  18%
  |                                                                            
  |=============                                                         |  18%
  |                                                                            
  |=============                                                         |  19%
  |                                                                            
  |==============                                                        |  19%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |==============                                                        |  21%
  |                                                                            
  |===============                                                       |  21%
  |                                                                            
  |===============                                                       |  22%
  |                                                                            
  |================                                                      |  22%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |=================                                                     |  24%
  |                                                                            
  |=================                                                     |  25%
  |                                                                            
  |==================                                                    |  25%
  |                                                                            
  |==================                                                    |  26%
  |                                                                            
  |===================                                                   |  26%
  |                                                                            
  |===================                                                   |  27%
  |                                                                            
  |===================                                                   |  28%
  |                                                                            
  |====================                                                  |  28%
  |                                                                            
  |====================                                                  |  29%
  |                                                                            
  |=====================                                                 |  29%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |======================                                                |  31%
  |                                                                            
  |======================                                                |  32%
  |                                                                            
  |=======================                                               |  32%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |========================                                              |  34%
  |                                                                            
  |========================                                              |  35%
  |                                                                            
  |=========================                                             |  35%
  |                                                                            
  |=========================                                             |  36%
  |                                                                            
  |==========================                                            |  36%
  |                                                                            
  |==========================                                            |  37%
  |                                                                            
  |==========================                                            |  38%
  |                                                                            
  |===========================                                           |  38%
  |                                                                            
  |===========================                                           |  39%
  |                                                                            
  |============================                                          |  39%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |============================                                          |  41%
  |                                                                            
  |=============================                                         |  41%
  |                                                                            
  |=============================                                         |  42%
  |                                                                            
  |==============================                                        |  42%
  |                                                                            
  |==============================                                        |  43%
  |                                                                            
  |==============================                                        |  44%
  |                                                                            
  |===============================                                       |  44%
  |                                                                            
  |===============================                                       |  45%
  |                                                                            
  |================================                                      |  45%
  |                                                                            
  |================================                                      |  46%
  |                                                                            
  |=================================                                     |  46%
  |                                                                            
  |=================================                                     |  47%
  |                                                                            
  |=================================                                     |  48%
  |                                                                            
  |==================================                                    |  49%
  |                                                                            
  |===================================                                   |  49%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |====================================                                  |  51%
  |                                                                            
  |====================================                                  |  52%
  |                                                                            
  |=====================================                                 |  52%
  |                                                                            
  |=====================================                                 |  53%
  |                                                                            
  |=====================================                                 |  54%
  |                                                                            
  |======================================                                |  54%
  |                                                                            
  |======================================                                |  55%
  |                                                                            
  |=======================================                               |  55%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |========================================                              |  56%
  |                                                                            
  |========================================                              |  57%
  |                                                                            
  |========================================                              |  58%
  |                                                                            
  |=========================================                             |  58%
  |                                                                            
  |=========================================                             |  59%
  |                                                                            
  |==========================================                            |  59%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |==========================================                            |  61%
  |                                                                            
  |===========================================                           |  61%
  |                                                                            
  |===========================================                           |  62%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |=============================================                         |  64%
  |                                                                            
  |=============================================                         |  65%
  |                                                                            
  |==============================================                        |  65%
  |                                                                            
  |==============================================                        |  66%
  |                                                                            
  |===============================================                       |  66%
  |                                                                            
  |===============================================                       |  67%
  |                                                                            
  |===============================================                       |  68%
  |                                                                            
  |================================================                      |  68%
  |                                                                            
  |================================================                      |  69%
  |                                                                            
  |=================================================                     |  69%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |=================================================                     |  71%
  |                                                                            
  |==================================================                    |  71%
  |                                                                            
  |==================================================                    |  72%
  |                                                                            
  |===================================================                   |  72%
  |                                                                            
  |===================================================                   |  73%
  |                                                                            
  |===================================================                   |  74%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |====================================================                  |  75%
  |                                                                            
  |=====================================================                 |  75%
  |                                                                            
  |=====================================================                 |  76%
  |                                                                            
  |======================================================                |  76%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |======================================================                |  78%
  |                                                                            
  |=======================================================               |  78%
  |                                                                            
  |=======================================================               |  79%
  |                                                                            
  |========================================================              |  79%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |========================================================              |  81%
  |                                                                            
  |=========================================================             |  81%
  |                                                                            
  |=========================================================             |  82%
  |                                                                            
  |==========================================================            |  82%
  |                                                                            
  |==========================================================            |  83%
  |                                                                            
  |==========================================================            |  84%
  |                                                                            
  |===========================================================           |  84%
  |                                                                            
  |===========================================================           |  85%
  |                                                                            
  |============================================================          |  85%
  |                                                                            
  |============================================================          |  86%
  |                                                                            
  |=============================================================         |  86%
  |                                                                            
  |=============================================================         |  87%
  |                                                                            
  |=============================================================         |  88%
  |                                                                            
  |==============================================================        |  88%
  |                                                                            
  |==============================================================        |  89%
  |                                                                            
  |===============================================================       |  89%
  |                                                                            
  |===============================================================       |  90%
  |                                                                            
  |===============================================================       |  91%
  |                                                                            
  |================================================================      |  91%
  |                                                                            
  |================================================================      |  92%
  |                                                                            
  |=================================================================     |  92%
  |                                                                            
  |=================================================================     |  93%
  |                                                                            
  |=================================================================     |  94%
  |                                                                            
  |==================================================================    |  94%
  |                                                                            
  |==================================================================    |  95%
  |                                                                            
  |===================================================================   |  95%
  |                                                                            
  |===================================================================   |  96%
  |                                                                            
  |====================================================================  |  96%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |====================================================================  |  98%
  |                                                                            
  |===================================================================== |  98%
  |                                                                            
  |===================================================================== |  99%
  |                                                                            
  |======================================================================|  99%
  |                                                                            
  |======================================================================| 100%
nc_map
## Simple feature collection with 100 features and 17 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -84.32182 ymin: 33.75288 xmax: -75.40012 ymax: 36.58814
## Geodetic CRS:  NAD83
## First 10 features:
##     STATEFP COUNTYFP COUNTYNS GEOID     NAME        NAMELSAD LSAD CLASSFP MTFCC
## 24       37      037 01008544 37037  Chatham  Chatham County   06      H1 G4020
## 60       37      001 01008531 37001 Alamance Alamance County   06      H1 G4020
## 92       37      057 01008548 37057 Davidson Davidson County   06      H1 G4020
## 183      37      069 01008553 37069 Franklin Franklin County   06      H1 G4020
## 220      37      155 01026130 37155  Robeson  Robeson County   06      H1 G4020
## 225      37      109 01008569 37109  Lincoln  Lincoln County   06      H1 G4020
## 234      37      027 01008541 37027 Caldwell Caldwell County   06      H1 G4020
## 263      37      063 01008550 37063   Durham   Durham County   06      H1 G4020
## 292      37      145 01008577 37145   Person   Person County   06      H1 G4020
## 306      37      115 01025834 37115  Madison  Madison County   06      H1 G4020
##     CSAFP CBSAFP METDIVFP FUNCSTAT      ALAND   AWATER    INTPTLAT     INTPTLON
## 24    450  20500     <NA>        A 1765540362 70582736 +35.7049939 -079.2514542
## 60    268  15500     <NA>        A 1096736059 27940532 +36.0439535 -079.4005733
## 92    268  49180     <NA>        A 1432728609 37604804 +35.7951312 -080.2071070
## 183   450  39580     <NA>        A 1273761673  7173998 +36.0882406 -078.2830903
## 220   246  31300     <NA>        A 2453481924  5076395 +34.6392096 -079.1008811
## 225   172  16740     <NA>        A  766239897 23350160 +35.4884909 -081.2268928
## 234  <NA>  25860     <NA>        A 1222184252  7052759 +35.9663959 -081.5125404
## 263   450  20500     <NA>        A  742303736 29156730 +36.0338282 -078.8781246
## 292   450  20500     <NA>        A 1016154889 31205409 +36.3863559 -078.9656305
## 306   120  11700     <NA>        A 1164498325  4840167 +35.8642058 -082.7126309
##                           geometry
## 24  MULTIPOLYGON (((-79.24113 3...
## 60  MULTIPOLYGON (((-79.43277 3...
## 92  MULTIPOLYGON (((-80.06476 3...
## 183 MULTIPOLYGON (((-78.11729 3...
## 220 MULTIPOLYGON (((-79.12835 3...
## 225 MULTIPOLYGON (((-81.08133 3...
## 234 MULTIPOLYGON (((-81.33647 3...
## 263 MULTIPOLYGON (((-78.80676 3...
## 292 MULTIPOLYGON (((-78.7998 36...
## 306 MULTIPOLYGON (((-82.6677 35...

We need to merge in the election data with the shape file. The nc_results data has an id field that 5 characters long, everyone starting with 37. The nc_map file has a STATEFP 2 character variable that is always 37, and COUNTYFP that is three characters long. These are the FIPS codes that are the standardized ids for US states and counties. So in order to merge the nc_shp polygons with the nc_results election data frame we will need to concatenate STATEFP and COUNTYFP into a new variable named id, then join the two datasets together.

nc_merged <- nc_map %>% 
   mutate(id = str_c(STATEFP, COUNTYFP)) %>% # creating a new ID that puts the state and county one together 
   left_join(nc_results, by = "id")

glimpse(nc_merged)
## Rows: 100
## Columns: 22
## $ STATEFP      <chr> "37", "37", "37", "37", "37", "37", "37", "37", "37", "37…
## $ COUNTYFP     <chr> "037", "001", "057", "069", "155", "109", "027", "063", "…
## $ COUNTYNS     <chr> "01008544", "01008531", "01008548", "01008553", "01026130…
## $ GEOID        <chr> "37037", "37001", "37057", "37069", "37155", "37109", "37…
## $ NAME         <chr> "Chatham", "Alamance", "Davidson", "Franklin", "Robeson",…
## $ NAMELSAD     <chr> "Chatham County", "Alamance County", "Davidson County", "…
## $ LSAD         <chr> "06", "06", "06", "06", "06", "06", "06", "06", "06", "06…
## $ CLASSFP      <chr> "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1", "H1…
## $ MTFCC        <chr> "G4020", "G4020", "G4020", "G4020", "G4020", "G4020", "G4…
## $ CSAFP        <chr> "450", "268", "268", "450", "246", "172", NA, "450", "450…
## $ CBSAFP       <chr> "20500", "15500", "49180", "39580", "31300", "16740", "25…
## $ METDIVFP     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ FUNCSTAT     <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A…
## $ ALAND        <dbl> 1765540362, 1096736059, 1432728609, 1273761673, 245348192…
## $ AWATER       <dbl> 70582736, 27940532, 37604804, 7173998, 5076395, 23350160,…
## $ INTPTLAT     <chr> "+35.7049939", "+36.0439535", "+35.7951312", "+36.0882406…
## $ INTPTLON     <chr> "-079.2514542", "-079.4005733", "-080.2071070", "-078.283…
## $ id           <chr> "37037", "37001", "37057", "37069", "37155", "37109", "37…
## $ state        <fct> NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC, N…
## $ name         <chr> "Chatham County", "Alamance County", "Davidson County", "…
## $ per_gop_2016 <dbl> 0.4353122, 0.5520184, 0.7341730, 0.5457829, 0.5143712, 0.…
## $ geometry     <MULTIPOLYGON [°]> MULTIPOLYGON (((-79.24113 3..., MULTIPOLYGON…

We can then use Leaflet. First we will define a color palette over the values [0,1] that ranges from red to blue. According to the documentation, colorNumeric():

Conveniently maps data values (numeric or factor/character) to colors according to a given palette, which can be provided in a variety of formats.

The palette argument can be any of the following:

  • A character vector of RGB or named colors. Examples: c(“#000000”, “#0000FF”, “#FFFFFF”)
  • The name of an RColorBrewer palette, e.g. “BuPu” or “Greens”.
  • The full name of a viridis palette: “viridis”, “magma”, “inferno”, or “plasma”.
  • A function that receives a single value between 0 and 1 and returns a color. Examples: colorRamp(c(“#000000”, “#FFFFFF”), interpolate = “spline”).

The domain parameter tells it the possible values that can be mapped. Once created it, you’ll see that it simply returns a function.

pal <- colorNumeric(palette = "RdBu", domain = c(0,1))

pal
## function (x) 
## {
##     if (length(x) == 0 || all(is.na(x))) {
##         return(pf(x))
##     }
##     if (is.null(rng)) 
##         rng <- range(x, na.rm = TRUE)
##     rescaled <- scales::rescale(x, from = rng)
##     if (any(rescaled < 0 | rescaled > 1, na.rm = TRUE)) 
##         warning("Some values were outside the color scale and will be treated as NA")
##     if (reverse) {
##         rescaled <- 1 - rescaled
##     }
##     pf(rescaled)
## }
## <bytecode: 0x12db4ce28>
## <environment: 0x12db1e6b0>
## attr(,"colorType")
## [1] "numeric"
## attr(,"colorArgs")
## attr(,"colorArgs")$na.color
## [1] "#808080"

To make the plot in Leaflet, we have to add the tiles, and then the polygons defined by the sf object nc_merged. Since it is already an SF object, we do not need to give it any explicit polygon arguments in terms of X and Y. Instead, we need to manipulate the weight, fillOpacity, and color, while also designating the text of the popup.

  • The weight controls the stroke width in pixels.
  • The color indicates what the border color should be.
  • fillColor is how the polygons should be colored; here is where we call the function we created above.
  • The fillOpacity does what you think it would, functioning essentially as an alpha argument. Since we chose a Red to Blue color pallete that mapped from 0 to 1, we actually need to flip the variable in order to associate higher values with Red. Thus we map ‘1-per_gop_2016’ to color; notice how we put a tilde in front of it to indicate that it is a function call.
  • The popup argument is also a function since it will vary based on the object. That function creates text that shows the district number and the proportion of Republican votes. Notice that to do a line break here, we have to use HTML code </br> because this is an interactive viz rendered in HTML.
leaflet_nc <- leaflet(nc_merged) %>% 
   addProviderTiles(providers$CartoDB.Positron) %>% 
   addPolygons(
      weight = 1,
      color = "grey",
      fillColor = ~pal(1-per_gop_2016),
      fillOpacity = 0.9,
      popup = ~str_c("<b>", name, "</b></br>", "GOP = ", round(per_gop_2016 * 100, 0), "%")) %>% 
   setView(lng = -80, lat = 35, zoom = 7)

leaflet_nc

We might want to add a state border here. To do so, we’ll need to get a state sf object using tigris, and then we simply add another addPolygons function call, this time pointing it specifically at the state sf object. We can set some options like the weight and color of the border, but the most important thing to do here is to tell it NOT to fill the object because that would obstruct our county fills.

nc_state_map <- states() %>% 
   filter(NAME == "North Carolina")
## Retrieving data for the year 2021
## 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |=                                                                     |   1%
  |                                                                            
  |==                                                                    |   3%
  |                                                                            
  |===                                                                   |   4%
  |                                                                            
  |====                                                                  |   6%
  |                                                                            
  |=====                                                                 |   7%
  |                                                                            
  |=====                                                                 |   8%
  |                                                                            
  |======                                                                |   8%
  |                                                                            
  |======                                                                |   9%
  |                                                                            
  |=======                                                               |  10%
  |                                                                            
  |========                                                              |  11%
  |                                                                            
  |========                                                              |  12%
  |                                                                            
  |=========                                                             |  12%
  |                                                                            
  |=========                                                             |  13%
  |                                                                            
  |===========                                                           |  16%
  |                                                                            
  |============                                                          |  17%
  |                                                                            
  |============                                                          |  18%
  |                                                                            
  |=============                                                         |  18%
  |                                                                            
  |=============                                                         |  19%
  |                                                                            
  |==============                                                        |  19%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |==============                                                        |  21%
  |                                                                            
  |===============                                                       |  22%
  |                                                                            
  |================                                                      |  22%
  |                                                                            
  |================                                                      |  23%
  |                                                                            
  |=================                                                     |  24%
  |                                                                            
  |=================                                                     |  25%
  |                                                                            
  |==================                                                    |  25%
  |                                                                            
  |==================                                                    |  26%
  |                                                                            
  |===================                                                   |  26%
  |                                                                            
  |===================                                                   |  27%
  |                                                                            
  |===================                                                   |  28%
  |                                                                            
  |====================                                                  |  28%
  |                                                                            
  |====================                                                  |  29%
  |                                                                            
  |=====================                                                 |  30%
  |                                                                            
  |=====================                                                 |  31%
  |                                                                            
  |======================                                                |  31%
  |                                                                            
  |=======================                                               |  32%
  |                                                                            
  |=======================                                               |  33%
  |                                                                            
  |========================                                              |  34%
  |                                                                            
  |========================                                              |  35%
  |                                                                            
  |=========================                                             |  35%
  |                                                                            
  |=========================                                             |  36%
  |                                                                            
  |==========================                                            |  36%
  |                                                                            
  |==========================                                            |  37%
  |                                                                            
  |==========================                                            |  38%
  |                                                                            
  |===========================                                           |  38%
  |                                                                            
  |===========================                                           |  39%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |=============================                                         |  42%
  |                                                                            
  |==============================                                        |  43%
  |                                                                            
  |==============================                                        |  44%
  |                                                                            
  |===============================                                       |  44%
  |                                                                            
  |===============================                                       |  45%
  |                                                                            
  |================================                                      |  45%
  |                                                                            
  |================================                                      |  46%
  |                                                                            
  |=================================                                     |  47%
  |                                                                            
  |==================================                                    |  48%
  |                                                                            
  |==================================                                    |  49%
  |                                                                            
  |===================================                                   |  50%
  |                                                                            
  |====================================                                  |  51%
  |                                                                            
  |====================================                                  |  52%
  |                                                                            
  |=====================================                                 |  52%
  |                                                                            
  |=====================================                                 |  53%
  |                                                                            
  |======================================                                |  54%
  |                                                                            
  |======================================                                |  55%
  |                                                                            
  |=======================================                               |  56%
  |                                                                            
  |========================================                              |  57%
  |                                                                            
  |========================================                              |  58%
  |                                                                            
  |=========================================                             |  58%
  |                                                                            
  |=========================================                             |  59%
  |                                                                            
  |==========================================                            |  59%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |===========================================                           |  61%
  |                                                                            
  |============================================                          |  62%
  |                                                                            
  |============================================                          |  63%
  |                                                                            
  |=============================================                         |  64%
  |                                                                            
  |==============================================                        |  66%
  |                                                                            
  |================================================                      |  68%
  |                                                                            
  |================================================                      |  69%
  |                                                                            
  |=================================================                     |  70%
  |                                                                            
  |=================================================                     |  71%
  |                                                                            
  |==================================================                    |  71%
  |                                                                            
  |==================================================                    |  72%
  |                                                                            
  |===================================================                   |  73%
  |                                                                            
  |====================================================                  |  74%
  |                                                                            
  |=====================================================                 |  76%
  |                                                                            
  |======================================================                |  77%
  |                                                                            
  |=======================================================               |  78%
  |                                                                            
  |=======================================================               |  79%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |========================================================              |  81%
  |                                                                            
  |=========================================================             |  81%
  |                                                                            
  |=========================================================             |  82%
  |                                                                            
  |==========================================================            |  83%
  |                                                                            
  |===========================================================           |  84%
  |                                                                            
  |===========================================================           |  85%
  |                                                                            
  |============================================================          |  85%
  |                                                                            
  |==================================================================    |  94%
  |                                                                            
  |===================================================================   |  95%
  |                                                                            
  |===================================================================   |  96%
  |                                                                            
  |====================================================================  |  96%
  |                                                                            
  |====================================================================  |  97%
  |                                                                            
  |===================================================================== |  98%
  |                                                                            
  |===================================================================== |  99%
  |                                                                            
  |======================================================================| 100%
leaflet_nc <- leaflet(nc_merged) %>% 
   addProviderTiles(providers$CartoDB.Positron) %>% 
   addPolygons(
      weight = 1,
      color = "grey",
      fillColor = ~pal(1-per_gop_2016),
      fillOpacity = 0.9,
      popup = ~str_c("<b>", name, "</b></br>", "GOP = ", round(per_gop_2016 * 100, 0), "%")) %>% 
   addPolygons(
      data = nc_state_map,
      weight = 2, 
      color = "#000000", 
      fill = FALSE) %>% # Do not fill the inside of the state; only draw the border
   setView(lng = -80, lat = 35, zoom = 7)
## Warning: sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
## Need '+proj=longlat +datum=WGS84'

## Warning: sf layer has inconsistent datum (+proj=longlat +datum=NAD83 +no_defs).
## Need '+proj=longlat +datum=WGS84'
leaflet_nc

Plotly Interactive Graphics

ggplotly is a library built and maintained by Plotly that allows you to convert any ggplot visualization into a plotly visualization using the ggplotly() function. It’s actually quite straightforward for basic visualizations.

Below we create our lollipop chart from the bivariate lesson.

i1 <- gapminder %>%
  filter(continent == "Asia" & 
           year == 2007) %>% 
  ggplot(aes(lifeExp, reorder(country, lifeExp))) +
  geom_segment(aes(x = 40, 
               xend = lifeExp, 
               y = reorder(country, lifeExp), 
               yend = reorder(country, lifeExp)),
               color = "lightgrey") +
  geom_point(color="darkred", size = 2) +
  labs (x = "Life Expectancy (years)",
        y = "",
        title = "Life Expectancy by Country",
        subtitle = "Gapminder data for Asia - 2007") +
  theme_minimal() + 
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

i1

All you need to do is pass it the ggplotly() function and it creates an interactive graphic. Notice the interactive controls that appear in the upper right corner of the graphic, as well as the hover text you get as you pass over the graphic.

library(plotly)
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
ggplotly(i1)

We can clean up the labeling - the easiest way to do this is to add the labeling you want to your dataset and then pass it as a text aesthetic in your initial ggplot call, you then use the aesthetic name as your tooltip parameter (NOT the var name!).

i1 <- gapminder %>%
  filter(continent == "Asia" & 
           year == 2007) %>% 
   mutate(label_text = str_c(country, ": ", round(lifeExp, 1))) %>% 
  ggplot(aes(lifeExp, reorder(country, lifeExp), text = label_text)) +
  geom_segment(aes(x = 40, 
               xend = lifeExp, 
               y = reorder(country, lifeExp), 
               yend = reorder(country, lifeExp)),
               color = "lightgrey") +
  geom_point(color="darkred", size = 2) +
  labs (x = "Life Expectancy (years)",
        y = "",
        title = "Life Expectancy by Country",
        subtitle = "Gapminder data for Asia - 2007") +
  theme_minimal() + 
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

ggplotly(i1, tooltip = "label_text")

Here’s another example with a scatterplot. Notice the mutate statement that gets us a nice label, and that it is mapped to the text aesthetic. The \n tells it to go to a new line.

library(scales)
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
i2 <- gapminder %>% 
   mutate(label_text = str_c(country, " ", year, ": \nGDP per cap = ", dollar(round(gdpPercap, 0)), "\nLife Exp = ", round(lifeExp, 1))) %>% 
   ggplot(aes(gdpPercap, lifeExp, text = label_text)) +
   geom_point() +
   scale_x_log10()

i2

We call ggplotly and tell it what aesthetic mapping to use for the tooltip.

ggplotly(i2, tooltip = "label_text")

And one more, this time with a time series. One funky thing here is that I needed to add a group = 1 aesthetic once I added the text aesthetic. I needed to tell ggplot that there was only one group of data - when it saw the text aesthetic it thought there were many.

i3 <- economics %>% 
   mutate(label_text = str_c(date, ": ", psavert, "%")) %>% 
   ggplot(aes(x = date, y = psavert, text = label_text, group = 1)) +
  geom_line(color = "indianred3", 
            size=1 ) +
  geom_smooth() +
  scale_x_date(date_breaks = '5 years', 
               labels = label_date("%b-%y")) +
  labs(title = "Personal Savings Rate",
       subtitle = "1967 to 2015",
       x = "",
       y = "Personal Savings Rate") +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
ggplotly(i3, tooltip = "label_text")
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation: text
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

You can also do direct Plotly functions, skipping ggplot entirely. This is especially useful when they have a chart format that isn’t easily available in ggplot, such as a stock candlestick chart. Below, I use the tidyquant library to easily get stock information for Google, which I then pass into a plot_ly function.

library(tidyquant)

prices <- tq_get("GOOGL")

prices %>%
   plot_ly(x = ~date,
           type = "candlestick",
           open = ~open,
           close = ~close,
           high = ~high,
           low = ~low, 
           split = ~symbol)

For more on Plotly you can use this cheat sheet, or you can visit the Plotly R Open Source Graphing Library.